﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using SimpleBoard.Compiler.SourceCodeGenerator;
using SimpleBoard.Project;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace SimpleBoard.Compiler
{
    public class ProjectCompiler
    {
        private readonly ProjectInfo project;

        public ProjectCompiler(ProjectInfo project)
        {
            this.project = project;
        }

        public string CompilePageGenerator()
        {
            var assemblyLocations = new HashSet<string>();

            ISourceCodeGenerator pageCodeGenerator = new PageSourceGenerator(project.Page, project.DataBindings);
            foreach (var ass in pageCodeGenerator.GetAssemblies())
                assemblyLocations.Add(ass);

            var systemAssemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location)!;
            assemblyLocations.Add(Path.Combine(systemAssemblyPath, "System.Runtime.dll"));

            var assemblies = assemblyLocations.Select(x => MetadataReference.CreateFromFile(x));

            var pageGeneratorSource = pageCodeGenerator.GenerateSourceCode();
            var pageGeneratorSyntaxTree = CSharpSyntaxTree.ParseText(pageGeneratorSource, new CSharpParseOptions(LanguageVersion.Latest));
            
            var assemblyName = "SimpleBoard.Binary.Generated";

            var compilation = CSharpCompilation.Create(assemblyName)
                                .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
                                .AddReferences(assemblies)
                                .AddSyntaxTrees(pageGeneratorSyntaxTree);
            var outputPath = Path.GetTempFileName();
            File.Delete(outputPath);

            var compilationResult = compilation.Emit(outputPath);
            if (compilationResult.Success) return outputPath;
            else throw new ArgumentException("编译错误");
        }

        public string CompileDataFrameMeta()
        {
            var outputPath = Path.GetTempFileName();
            using var file = File.OpenWrite(outputPath);
            using var writer = new StreamWriter(file);
            writer.WriteLine(JsonSerializer.Serialize(project.DataFrameMeta));
            writer.Flush();
            writer.Close();
            return outputPath;
        }

        public CompileResult Compile()
        {
            var pageGenerator = this.CompilePageGenerator();
            var dataProtocol = this.CompileDataFrameMeta();

            return new CompileResult(pageGenerator, dataProtocol);
        }
    }
}
